/*
 * Time : 2022/12/3 19:14
 * Auth : weixinlong
 * Email : weixinlong@buaa.edu.cn
 * File : user.go
 */

package handler

import (
	"context"
	"errors"
	"fmt"
	"hertz/demo/biz/model"
	"hertz/demo/biz/mw"
	"hertz/demo/biz/util"
	"net/http"
	"strconv"

	"github.com/cloudwego/hertz/pkg/app"
	"github.com/cloudwego/hertz/pkg/common/hlog"
	"github.com/cloudwego/hertz/pkg/common/utils"
	"gorm.io/gorm"
)

func Register(ctx context.Context, c *app.RequestContext) {
	type Req struct {
		Username string `json:"username"`
		Password string `json:"password"`
		IsAdmin  bool   `json:"is_admin"`
	}
	// BindAndValidate
	var req Req
	err := c.BindAndValidate(&req)
	if err != nil {
		util.ResponseFail(c, "bind fail")
		return
	}
	hlog.CtxDebugf(ctx, fmt.Sprintf("username: %s", req.Username))

	var users []model.User
	mw.GetDB(c).Table("users").Where("username = ?", req.Username).Find(&users)
	if len(users) != 0 {
		hlog.CtxErrorf(ctx, "user already exist: %s", req.Username)
		util.ErrMessage(c, http.StatusBadRequest, "user already exist")
		return
	}

	record := &model.User{Username: req.Username, Password: req.Password, IsAdmin: req.IsAdmin}
	if err := model.AddUser(mw.GetDB(c), record); err != nil {
		hlog.CtxErrorf(ctx, "failed to create user; %w", err)
		util.ErrMessage(c, http.StatusInternalServerError, "failed to create user")
		return
	}

	// return created user id
	c.JSON(http.StatusCreated, utils.H{
		"id": record.ID,
	})
}

func GetUserById(ctx context.Context, c *app.RequestContext) {
	id := c.Param("id")
	if id == "" {
		util.ErrMessage(c, http.StatusBadRequest, "can't find user id")
		return
	}

	userId, err := strconv.ParseUint(id, 10, 64)
	if err != nil {
		hlog.CtxDebugf(ctx, "can't parse user id: %s", err.Error())
		util.ErrMessage(c, http.StatusBadRequest, "can't parse user id")
		return
	}

	user, err := model.FindUser(mw.GetDB(c), uint(userId))
	if err != nil {
		if errors.Is(err, gorm.ErrRecordNotFound) {
			util.ErrMessage(c, http.StatusNotFound, "can't find user")
		} else {
			hlog.CtxErrorf(ctx, "can't parse user id: %w", err)
			util.ErrMessage(c, http.StatusInternalServerError, "db error")
		}
		return
	}

	user.Password = ""

	c.JSON(http.StatusOK, user)
}

func GetAllUser(ctx context.Context, c *app.RequestContext) {
	users, err := model.ListUser(mw.GetDB(c))
	if err != nil {
		hlog.CtxErrorf(ctx, "can't list user from db: %w", err)
		util.ErrMessage(c, http.StatusInternalServerError, "db error")
	}

	for i := range users {
		users[i].Password = ""
	}
	c.JSON(http.StatusOK, users)
}

func EnableUser(ctx context.Context, c *app.RequestContext) {
	id := c.Param("id")
	if id == "" {
		util.ErrMessage(c, http.StatusBadRequest, "can't find user id")
		return
	}

	userId, err := strconv.ParseUint(id, 10, 64)
	if err != nil {
		hlog.Debugf("can't parse user id: %s", err.Error())
		util.ErrMessage(c, http.StatusBadRequest, "can't parse user id")
		return
	}

	if err := model.EnableUser(mw.GetDB(c), uint(userId)); err != nil {
		if errors.Is(err, gorm.ErrRecordNotFound) {
			util.ErrMessage(c, http.StatusNotFound, "user not found")
		} else {
			hlog.CtxErrorf(ctx, "can't enable user, user_id: %d : %w", userId, err)
			util.ErrMessage(c, http.StatusInternalServerError, "db error")
		}

	}

	c.Status(http.StatusOK)
}

func UpdateUser(ctx context.Context, c *app.RequestContext) {
	var user model.User
	if err := c.BindAndValidate(&user); err != nil {
		util.ErrMessage(c, http.StatusBadRequest, err.Error())
	}

	err := model.UpdateUser(mw.GetDB(c), &user)
	if err != nil {
		if errors.Is(err, gorm.ErrRecordNotFound) {
			util.ErrMessage(c, http.StatusNotFound, "user not found")
		} else {
			util.ErrMessage(c, http.StatusInternalServerError, "db error")
		}
		return
	}
	c.Status(http.StatusOK)
}

func DisableUser(ctx context.Context, c *app.RequestContext) {
	id := c.Param("id")
	if id == "" {
		util.ErrMessage(c, http.StatusBadRequest, "can't find user id")
		return
	}

	userId, err := strconv.ParseUint(id, 10, 64)
	if err != nil {
		hlog.Debugf("can't parse user id: %s", err.Error())
		util.ErrMessage(c, http.StatusBadRequest, "can't parse user id")
		return
	}

	if err := model.DisableUser(mw.GetDB(c), uint(userId)); err != nil {
		if errors.Is(err, gorm.ErrRecordNotFound) {
			util.ErrMessage(c, http.StatusNotFound, "user not found")
		} else {
			hlog.CtxErrorf(ctx, "can't enable user, user_id: %d : %w", userId, err)
			util.ErrMessage(c, http.StatusInternalServerError, "db error")
		}

	}

	c.Status(http.StatusOK)
}

func LogIn(ctx context.Context, c *app.RequestContext) {
	type Req struct {
		UserName string `json:"username"`
		PassWord string `json:"password"`
	}
	// BindAndValidate
	var req Req
	err := c.BindAndValidate(&req)
	if err != nil {
		util.ResponseFail(c, "bind fail")
		return
	}

	var users []model.User
	mw.GetDB(c).Table("users").Where("username = ?", req.UserName).Find(&users)
	if len(users) == 0 {
		util.ResponseFail(c, "user not exist")
		return
	}

	match := util.CheckPasswordHash(req.PassWord, users[0].Password)
	if match {
		util.ResponseSuccess(c, "null")
	} else {
		util.ResponseFail(c, "password not correct")
	}

}

func UpdatePassword(ctx context.Context, c *app.RequestContext) {
	type Req struct {
		Password string `json:"password"`
	}

	var req Req
	err := c.BindAndValidate(&req)
	if err != nil {
		util.ErrMessage(c, http.StatusBadRequest, err.Error())
		return
	}

	id := c.Param("id")
	if id == "" {
		util.ErrMessage(c, http.StatusBadRequest, "can't find user id")
		return
	}

	userId, err := strconv.ParseUint(id, 10, 64)
	if err != nil {
		hlog.Debugf("can't parse user id: %s", err.Error())
		util.ErrMessage(c, http.StatusBadRequest, "can't parse user id")
		return
	}

	err = model.ResetUserPassword(mw.GetDB(c), uint(userId), req.Password)
	if err != nil {
		if errors.Is(err, gorm.ErrRecordNotFound) {
			util.ErrMessage(c, http.StatusNotFound, "user not found")
		} else {
			hlog.CtxErrorf(ctx, "reset user password error: %w", err)
			util.ErrMessage(c, http.StatusInternalServerError, "db error")
		}
		return
	}

	c.Status(http.StatusOK)
}
